﻿using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using static systeIoCompressionZipFile.MainWindow;

namespace systeIoCompressionZipFile
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }



        private void Compress_Click(object sender, RoutedEventArgs e)
        {

            cprogress.Value = 0;
            IProgress<double> progress = new Progress<double>(value =>
            {
                cprogress.Value = value;
            });
            //ZipFile.CreateFromDirectory(@".\start", @".\result.zip");
            Thread thread = new Thread(() =>
            {

                ZipFileWithProgressBar(@".\start", @".\result.zip", progress);
            });
            thread.IsBackground = true;
            thread.Start();
        }

        private void Decompress_Click(object sender, RoutedEventArgs e)
        {
            //ZipFile.ExtractToDirectory(@".\result.zip", @".\start");

            cprogress.Value = 0;
            IProgress<double> progress = new Progress<double>(value =>
            {
                cprogress.Value = value;
            });
            //ZipFile.CreateFromDirectory(@".\start", @".\result.zip");
            Thread thread = new Thread(() =>
            {

                UnZipWithProgressBar(@".\result.zip", @".\start", progress);
                //ExtractAllZipFileOfFolder(string zipFileDirectory, string targetDirectory, ref List < FileUnZipSize > unZipProgresses);
            });
            thread.IsBackground = true;
            thread.Start();
        }


        /// <summary>
        /// 压缩指定目录到zip文件，并在压缩过程中报告进度
        /// </summary>
        /// <param name="sourceDirectory"></param>
        /// <param name="destinationZipFilePath"></param>
        /// <param name="progress"></param>
        /// <exception cref="DirectoryNotFoundException"></exception>
        public static void ZipFileWithProgressBar(string sourceDirectory, string destinationZipFilePath, IProgress<double> progress)
        {
            if (!Directory.Exists(sourceDirectory))
                throw new DirectoryNotFoundException($"Source directory not found: {sourceDirectory}");

            var files = Directory.GetFiles(sourceDirectory, "*", SearchOption.AllDirectories);
            long totalSize = 0;
            foreach (var file in files)
            {
                var fileInfo = new FileInfo(file);
                totalSize += fileInfo.Length;
            }

            long totalProcessed = 0;
            using (var zipStream = new FileStream(destinationZipFilePath, FileMode.Create))
            using (var archive = new ZipArchive(zipStream, ZipArchiveMode.Create))
            {
                foreach (var file in files)
                {
                    var fileInfo = new FileInfo(file);
                    var entry = archive.CreateEntry(file.Substring(sourceDirectory.Length + 1), CompressionLevel.Optimal);

                    using (var entryStream = entry.Open())
                    using (var fileStream = File.OpenRead(file))
                    {
                        fileStream.CopyTo(entryStream);
                        totalProcessed += fileInfo.Length;
                        Console.WriteLine((double)totalProcessed / totalSize);
                        int progressInt = (int)((double)totalProcessed / totalSize * 100);

                        progress.Report(progressInt);
                    }
                }
            }
        }


        /// <summary>
        /// 压缩指定目录到zip文件，并在压缩过程中报告进度
        /// </summary>
        /// <param name="sourceDirectory"></param>
        /// <param name="destinationZipFilePath"></param>
        /// <param name="progress"></param>
        /// <exception cref="DirectoryNotFoundException"></exception>
        public static void ZipFileWithProgressBarWithSize(string sourceDirectory, string destinationZipFilePath, ref FileUnZipSize fileUnZipSize)
        {
            if (!Directory.Exists(sourceDirectory))
                throw new DirectoryNotFoundException($"Source directory not found: {sourceDirectory}");

            var files = Directory.GetFiles(sourceDirectory, "*", SearchOption.AllDirectories);
            long totalSize = 0;
            foreach (var file in files)
            {
                var fileInfo = new FileInfo(file);
                totalSize += fileInfo.Length;
            }

            long totalProcessed = 0;
            using (var zipStream = new FileStream(destinationZipFilePath, FileMode.Create))
            using (var archive = new ZipArchive(zipStream, ZipArchiveMode.Create))
            {
                foreach (var file in files)
                {
                    var fileInfo = new FileInfo(file);
                    var entry = archive.CreateEntry(file.Substring(sourceDirectory.Length + 1), CompressionLevel.Optimal);

                    using (var entryStream = entry.Open())
                    using (var fileStream = File.OpenRead(file))
                    {
                        fileStream.CopyTo(entryStream);
                        totalProcessed += fileInfo.Length;
                        fileUnZipSize.TotalProcessed = totalProcessed;
                        fileUnZipSize.TotalSize = totalSize;
                    }
                }
            }
        }


        /// <summary>
        /// 解压zip文件到指定目录，并在解压过程中报告进度
        /// </summary>
        /// <param name="zipFilePath"></param>
        /// <param name="extractPath"></param>
        /// <param name="progress"></param>
        /// <exception cref="InvalidOperationException"></exception>
        public static void UnZipWithProgressBar(string zipFilePath, string extractPath, IProgress<double> progress)
        {
            // 确保解压目标路径存在
            Directory.CreateDirectory(extractPath);

            using (var archive = ZipFile.OpenRead(zipFilePath))
            {
                var totalEntries = archive.Entries.Count;
                int entriesProcessed = 0;

                foreach (var entry in archive.Entries)
                {
                    // 计算当前条目的目标路径
                    string destinationPath = System.IO.Path.GetFullPath(System.IO.Path.Combine(extractPath, entry.FullName));

                    // 检查目标路径是否真的在预期的解压缩目录下
                    if (!destinationPath.StartsWith(System.IO.Path.GetFullPath(extractPath), StringComparison.OrdinalIgnoreCase))
                    {
                        throw new InvalidOperationException($"Attempt to extract files outside the target directory: {destinationPath}");
                    }

                    if (string.IsNullOrEmpty(entry.Name))
                    {
                        // 条目是一个目录
                        if (!Directory.Exists(destinationPath))
                        {
                            Directory.CreateDirectory(destinationPath);
                        }
                    }
                    else
                    {
                        // 条目是一个文件
                        // 确保文件所在的目录存在
                        Directory.CreateDirectory(System.IO.Path.GetDirectoryName(destinationPath));
                        entry.ExtractToFile(destinationPath, true);
                    }

                    entriesProcessed++;
                    int progressInt = (int)((double)entriesProcessed / totalEntries * 100);
                    progress?.Report(progressInt);
                }
            }
        }

        /// <summary>
        /// 解压情况报文
        /// </summary>
        public class FileUnZipSize
        {
            public double TotalSize { get; set; }
            public double TotalProcessed { get; set; }
        }
        /// <summary>
        /// 解压zip文件到指定目录，并在解压过程中报告进度
        /// </summary>
        /// <param name="zipFilePath"></param>
        /// <param name="extractPath"></param>
        /// <param name="progress"></param>
        /// <exception cref="InvalidOperationException"></exception>
        public static void UnZipWithSize(string zipFilePath, string extractPath, ref FileUnZipSize fileUnZipSize)
        {
            // 确保解压目标路径存在
            Directory.CreateDirectory(extractPath);

            using (var archive = ZipFile.OpenRead(zipFilePath))
            {
                var totalEntries = archive.Entries.Count;
                int entriesProcessed = 0;

                foreach (var entry in archive.Entries)
                {
                    // 计算当前条目的目标路径
                    string destinationPath = System.IO.Path.GetFullPath(System.IO.Path.Combine(extractPath, entry.FullName));

                    // 检查目标路径是否真的在预期的解压缩目录下
                    if (!destinationPath.StartsWith(System.IO.Path.GetFullPath(extractPath), StringComparison.OrdinalIgnoreCase))
                    {
                        throw new InvalidOperationException($"Attempt to extract files outside the target directory: {destinationPath}");
                    }

                    if (string.IsNullOrEmpty(entry.Name))
                    {
                        // 条目是一个目录
                        if (!Directory.Exists(destinationPath))
                        {
                            Directory.CreateDirectory(destinationPath);
                        }
                    }
                    else
                    {
                        // 条目是一个文件
                        // 确保文件所在的目录存在
                        Directory.CreateDirectory(System.IO.Path.GetDirectoryName(destinationPath));
                        entry.ExtractToFile(destinationPath, true);
                    }

                    entriesProcessed++;
                    fileUnZipSize.TotalProcessed = entriesProcessed;
                    fileUnZipSize.TotalSize = totalEntries;
                }
            }
        }



        /// <summary>
        /// 把指定文件夹中的所有ZIP文件解压缩到指定文件夹中
        /// </summary>
        /// <param name="folderPath">压缩文件所在文件夹</param>
        /// <param name="targetFolder">解压目标文件夹</param>
        public static void ExtractAllZipFileOfFolder(string folderPath, string targetFolder, ref List<FileUnZipSize> fileUnZipSizes)
        {
            // 获取文件夹中的所有ZIP文件
            string[] zipFiles = Directory.GetFiles(folderPath, "*.zip", SearchOption.TopDirectoryOnly);

            // 逐个解压缩ZIP文件
            foreach (string zipFile in zipFiles)
            {
                FileUnZipSize fileUnZipSize = new FileUnZipSize();
                fileUnZipSizes.Add(fileUnZipSize);
                Task.Run(() =>
                {
                    UnZipWithSize(zipFile, targetFolder, ref fileUnZipSize);
                });
            }
        }




        private void selecteZipfileDirectory_Click(object sender, RoutedEventArgs e)
        {

            var folderDialog = new OpenFileDialog
            {
                Title = "选择文件夹",
                Filter = "文件夹|*.dummy",
                CheckFileExists = false,
                CheckPathExists = true,
                FileName = "选择文件夹"
            };

            if (folderDialog.ShowDialog() == true)
            {
                // 获取文件夹路径
                string selectedFolder = System.IO.Path.GetDirectoryName(folderDialog.FileName);
                zipfileDirectory.Text = selectedFolder;
            }

        }

        private void selectetargetfileDirectory_Click(object sender, RoutedEventArgs e)
        {

            var folderDialog = new OpenFileDialog
            {
                Title = "选择文件夹",
                Filter = "文件夹|*.dummy",
                CheckFileExists = false,
                CheckPathExists = true,
                FileName = "选择文件夹"
            };
            if (folderDialog.ShowDialog() == true)
            {
                // 获取文件夹路径
                string selectedFolder = System.IO.Path.GetDirectoryName(folderDialog.FileName);
                targetfileDirectory.Text = selectedFolder;
            }

        }

        private void selectezipfile_Click(object sender, RoutedEventArgs e)
        {

            var folderDialog = new OpenFileDialog
            {
                Title = "选择文件夹",
                Filter = "文件夹|*.dummy",
                CheckFileExists = false,
                CheckPathExists = true,
                FileName = "选择文件夹"
            };
            if (folderDialog.ShowDialog() == true)
            {
                // 获取文件夹路径
                string selectedFolder = System.IO.Path.GetDirectoryName(folderDialog.FileName);
                zipfileFolder.Text = selectedFolder;
            }
        }

        //////////////////////////////////////////////////////
        //////////////////////////////////////////////////////
        //////////////////////////////////////////////////////
        //////////////////////////////////////////////////////
        //////////////////////////////////////////////////////


        /// <summary>
        /// 分割文件夹并压缩
        /// </summary>
        /// <param name="sourceFolderPath">源文件夹路径</param>
        /// <param name="destinationFolderPath">目标文件夹路径</param>
        /// <param name="maxSizeMB">每个压缩文件最大尺寸（MB）</param>
        static void SplitAndCompressFolder(string sourceFolderPath, string destinationFolderPath, string zipFileNewName, int maxSizeMB, ref List<FileUnZipSize> fileUnZipSizes)
        {
            long totalSize = 0;
            DirectoryInfo sourceFolder = new DirectoryInfo(sourceFolderPath);
            List<FileInfo> files = new List<FileInfo>();
            int fileCount = 0;
            List<string> zipFileNames = new List<string>();
            foreach (FileInfo file in sourceFolder.GetFiles("*", SearchOption.AllDirectories))
            {
                if (totalSize + file.Length <= 1024 * 1024 * maxSizeMB)
                {
                    totalSize += file.Length;
                    files.Add(file);
                }
                else
                {
                    FileUnZipSize fileUnZipSize = new FileUnZipSize();
                    fileUnZipSizes.Add(fileUnZipSize);
                    zipFileNames.Add(CopyAndZipListFile(files, sourceFolderPath, destinationFolderPath, zipFileNewName, fileCount, ref fileUnZipSize));

                    // 重置变量
                    totalSize = 0;
                    fileCount++;
                    files.Clear();
                    // 添加当前这次
                    totalSize += file.Length; // 添加本次的文件的大小
                    files.Add(file); // 添加本次的文件   
                    continue;
                }
            }

            if (files.Count > 0)
            {
                FileUnZipSize fileUnZipSize = new FileUnZipSize();
                fileUnZipSizes.Add(fileUnZipSize);
                zipFileNames.Add(CopyAndZipListFile(files, sourceFolderPath, destinationFolderPath, zipFileNewName, fileCount, ref fileUnZipSize));
            }
        

            foreach (var item in zipFileNames)
            {
                Console.WriteLine(item);
                FileUnZipSize fileUnZipSize = new FileUnZipSize();
                fileUnZipSizes.Add(fileUnZipSize);
                Thread thread = new Thread(() =>
                {
                    ZipFileWithProgressBarWithSize(item, item + ".zip", ref fileUnZipSize);
                });
                thread.IsBackground = true;
                thread.Start();
            }
        }
        /// <summary>
        /// 复制文件并压缩
        /// </summary>
        /// <param name="files">文件列表</param>
        /// <param name="sourceFolderPath">源文件夹路径</param>
        /// <param name="destinationFolderPath">目标文件夹路径</param>
        /// <param name="index">文件夹索引</param>
        static string CopyAndZipListFile(List<FileInfo> files, string sourceFolderPath, string destinationFolderPath, string zipFileNewName, int index, ref FileUnZipSize fileUnZipSize)
        {
            string destinationFolderPathx = destinationFolderPath + "\\" + zipFileNewName + "_" + index;

            // 如果目标文件夹不存在，则创建
            if (!Directory.Exists(destinationFolderPathx))
            {
                Directory.CreateDirectory(destinationFolderPathx);

                // 把所有文件复制到指定文件夹，保留原有的文件夹结构
                foreach (FileInfo f in files)
                {
                    string relativePath = f.FullName.Substring(sourceFolderPath.Length + 1);
                    string destinationPath = System.IO.Path.Combine(destinationFolderPathx, relativePath);
                    string destinationFolder = System.IO.Path.GetDirectoryName(destinationPath);

                    // 确保目标文件夹存在
                    if (!Directory.Exists(destinationFolder))
                    {
                        Directory.CreateDirectory(destinationFolder);
                    }

                    // 复制文件到目标路径
                    File.Copy(f.FullName, destinationPath, true);
                }
            }

            Console.WriteLine("压缩文件夹：" + destinationFolderPathx);
            return destinationFolderPathx;
        }






        /// <summary>
        /// 分包压缩文件夹到指定文件夹为多个zip文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void compressFolderToFiles_Click(object sender, RoutedEventArgs e)
        {
            //根据设定的大小分包文件夹...
            string sourceFolderPath = "";
            string destinationFolderPath = "";
            string zipFileNewName = "";
            int maxSizeMB = 0;
            if (string.IsNullOrEmpty(zipfileDirectory.Text) || string.IsNullOrEmpty(targetfileDirectory.Text) || string.IsNullOrEmpty(zipfilename.Text) || string.IsNullOrEmpty(zipfilesize.Text))
            {
                MessageBox.Show("文件选择不全...");
                return;
            }

            sourceFolderPath = zipfileDirectory.Text;
            destinationFolderPath = targetfileDirectory.Text;
            zipFileNewName = zipfilename.Text;
            maxSizeMB = int.Parse(zipfilesize.Text);
            List<FileUnZipSize> fileUnZipSizes = new List<FileUnZipSize>();
            Thread thread1 = new Thread(() =>
            {
                SplitAndCompressFolder(sourceFolderPath, destinationFolderPath, zipFileNewName, maxSizeMB, ref fileUnZipSizes);
            });
            thread1.IsBackground = true;
            thread1.Start();
            cprogress.Value = 0;
            IProgress<double> progress = new Progress<double>(value =>
            {
                cprogress.Value = value;
            });


            Thread thread = new Thread(() =>
            {
                while (true)
                {
                    double totalSize = 0;
                    double totalProcessed = 0;
                    foreach (var item in fileUnZipSizes)
                    {
                        totalSize += item.TotalSize;
                        totalProcessed += item.TotalProcessed;
                    }
                    int progressInt = (int)((double)totalProcessed / totalSize * 100);
                    progress.Report(progressInt);
                    Console.WriteLine(progressInt);
                    if (progressInt == 100)
                    {
                        break;
                    }
                    Thread.Sleep(300);

                }
            });

            thread.IsBackground = true;
            thread.Start();
            Console.WriteLine("解压完成..........XXXXXXXXXXXXXXXXXXXXXXXX");

        }

        /// <summary>
        /// 压缩文件夹到一个zip文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void compressFolderToOneZipFile_Click(object sender, RoutedEventArgs e)
        {

            string sourceDirectory = "", destinationZipFilePath = "";

            cprogress.Value = 0;
            IProgress<double> progress = new Progress<double>(value =>
            {
                cprogress.Value = value;
            });

            if (string.IsNullOrEmpty(zipfileDirectory.Text))
            {
                MessageBox.Show("请选择文件夹");
                return;
            }
            sourceDirectory = zipfileDirectory.Text;
            //ZipFile.CreateFromDirectory(@".\start", @".\result.zip");
            Thread thread = new Thread(() =>
            {
                compressFolderToOneZipFile.Dispatcher.Invoke(() =>
                {
                    if (string.IsNullOrEmpty(zipfilename.Text))
                    {
                        zipfilename.Text = "zipFilenew";
                    }
                    if (string.IsNullOrEmpty(targetfileDirectory.Text))
                    {
                        //当前应用程序所在目录
                        targetfileDirectory.Text = System.AppDomain.CurrentDomain.BaseDirectory;
                    }
                    destinationZipFilePath = targetfileDirectory.Text + "\\" + zipfilename.Text + ".zip";
                });
                ZipFileWithProgressBar(sourceDirectory, destinationZipFilePath, progress);//sourceDirectory="", destinationZipFilePath=""   zipfileDirectory.Text, targetfileDirectory.Text + "\\" + zipfilename.Text + ".zip"
            });
            thread.IsBackground = true;
            thread.Start();
        }

        /// <summary>
        /// 解压zip文件到指定文件夹
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void unzipFileToOneFolder_Click(object sender, RoutedEventArgs e)
        {

            string sourceDirectory = "", destinationZipFilePath = "";
            if (string.IsNullOrEmpty(zipfilePath.Text) || string.IsNullOrEmpty(zipfileFolder.Text))
            {
                MessageBox.Show("请选择文件或文件夹..");
                return;
            }
            else
            {
                sourceDirectory = zipfilePath.Text;
                destinationZipFilePath = zipfileFolder.Text;
            }

            cprogress.Value = 0;
            IProgress<double> progress = new Progress<double>(value =>
            {
                cprogress.Value = value;
            });
            //ZipFile.CreateFromDirectory(@".\start", @".\result.zip");
            Thread thread = new Thread(() =>
            {

                UnZipWithProgressBar(sourceDirectory, destinationZipFilePath, progress);
                //ExtractAllZipFileOfFolder(string zipFileDirectory, string targetDirectory, ref List < FileUnZipSize > unZipProgresses);
            });
            thread.IsBackground = true;
            thread.Start();

        }

        /// <summary>
        /// 解压文件夹中的多个zip文件到指定文件夹
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void unzipFolderToFolder_Click(object sender, RoutedEventArgs e)
        {
            List<FileUnZipSize> fileUnZipSizes = new List<FileUnZipSize>();
            ExtractAllZipFileOfFolder(targetfileDirectory.Text, zipfileFolder.Text, ref fileUnZipSizes);
            cprogress.Value = 0;
            IProgress<double> progress = new Progress<double>(value =>
            {
                cprogress.Value = value;
            });


            Thread thread = new Thread(() =>
            {
                while (true)
                {
                    double totalSize = 0;
                    double totalProcessed = 0;
                    foreach (var item in fileUnZipSizes)
                    {
                        totalSize += item.TotalSize;
                        totalProcessed += item.TotalProcessed;
                    }
                    int progressInt = (int)((double)totalProcessed / totalSize * 100);
                    progress.Report(progressInt);
                    Console.WriteLine(progressInt);
                    if (progressInt == 100)
                    {
                        break;
                    }
                    Thread.Sleep(300);

                }
            });

            thread.IsBackground = true;
            thread.Start();
            Console.WriteLine("解压完成..........");
        }

        private void selectezipfilePath_Click(object sender, RoutedEventArgs e)
        {
            var fileDialog = new OpenFileDialog
            {
                Title = "选择ZIP文件",
                Filter = "ZIP文件 (*.zip)|*.zip", // 只显示ZIP文件
                CheckFileExists = true,
                CheckPathExists = true
            };
            if (fileDialog.ShowDialog() == true)
            {
                // 获取选中的ZIP文件路径
                string selectedZipFile = fileDialog.FileName;
                zipfilePath.Text = selectedZipFile; // 假设zipfile是一个文本框用于显示选中文件的路径
            }
        }
    }
}
